#include <jni.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>
#include <elf.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include "binder.h"
#define LOG_TAG "DEBUG"
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)

int (*old_ioctl)(int __fd, int __request, void * arg) = NULL;

int new_ioctl(int __fd, unsigned long int __request, void * arg)
{
    LOGD("In new_ioctl");
    int oldIoctl = old_ioctl(__fd, __request, arg);

    if(__request == BINDER_WRITE_READ)
    {
    	int dir  = _IOC_DIR(__request);  //根据命令获取传输方向
    	int type = _IOC_TYPE(__request); //根据命令获取类型
    	int nr   = _IOC_NR(__request);   //根据命令获取类型命令
        int size = _IOC_SIZE(__request); //根据命令获取传输数据大小
        //LOGD("new call to ioctl, dir:%d, type:%d, nr:%d, size:%d\n", dir, type, nr, size);
        struct binder_write_read *bwr;
        signed long int write_size = bwr->write_size;
        signed long int read_size  = bwr->read_size;

        if(write_size>0) //将write_buffer中的数据写入到binder
        {
        	LOGD("binder_write_read----->write size: %d,write_consumed :%d", bwr->write_size, bwr->write_consumed);
        	signed long int already_get_size = 0;
            unsigned long *pcmd = 0;
            LOGD("=====write_buffer process start!====");
            while(already_get_size < write_size)
            {
            	pcmd = (unsigned long *)(bwr->write_buffer+already_get_size); //指针后移动
                int code = pcmd[0];
                LOGD("pcmd: %x, already_got_size: %d", pcmd, already_get_size);

                int dir  =  _IOC_DIR(code);   //根据命令获取传输方向
                int type =  _IOC_TYPE(code);  //根据命令获取类型
                int nr   =  _IOC_NR(code);    //根据命令获取类型命令
                int size =  _IOC_SIZE(code);  //根据命令获取传输数据大小
                LOGD("cmdcode:%d, dir:%d, type:%c, nr:%d, size:%d\n", code, dir, type, nr, size);

                struct binder_transaction_data *pdata = (struct binder_transaction_data*)(&pcmd[1]);
                switch (code)
                {
                  case BC_TRANSACTION:
                       LOGD("pid: %d, BC_TRANSACTION, dir:%d, type:%c, nr:%d, size:%d\n", pdata->sender_pid, dir, type, nr, size);
                       parse_binder(pdata, 1);
                       break;

                  case BC_REPLY:
                       LOGD("pid: %d, BC_REPLY, dir:%d, type:%c, nr:%d, size:%d\n", pdata->sender_pid, dir, type, nr, size);
                       parse_binder(pdata, 1);
                       break;

                  default:
                       break;
                }
                already_get_size += (size+4);
            }
        }

        if(read_size > 0)//从binder中读取数据写入到read_buffer
        {
          LOGD("binder_write_read----->read size: %d, read_consumed: %d", bwr->read_size, bwr->read_consumed);
          int already_got_size = 0;
          unsigned long *pret = 0;

          LOGD("===read_buffer process start!====");
          while(already_got_size < read_size)//循环处理buffer中的每一个命令
          {
             pret = (unsigned long *)(bwr->read_buffer + already_got_size);    //指针后移
             int code = pret[0];
             LOGD("pret: %x, already_got_size: %d", pret, already_got_size);

             int dir  =  _IOC_DIR(code);   //根据命令获取传输方向
             int type =  _IOC_TYPE(code);  //根据命令获取类型
             int nr   =  _IOC_NR(code);    //根据命令获取类型命令
             int size =  _IOC_SIZE(code);  //根据命令获取传输数据大小
             LOGD("retcode:%d, dir:%d, type:%c, nr:%d, size:%d\n", code, dir, type, nr, size);

             struct binder_transaction_data* pdata = (struct binder_transaction_data*)(&pret[1]);
             switch (code)
             {
               case BR_TRANSACTION:
                    LOGD("pid: %d, BR_TRANSACTION, dir:%d, type:%c, nr:%d, size:%d\n", pdata->sender_pid, dir, type, nr, size);
                    parse_binder(pdata, 2);
                    break;

               case BR_REPLY:
                    LOGD("pid: %d, BR_REPLY, dir:%d, type:%c, nr:%d, size:%d\n", pdata->sender_pid, dir, type, nr, size);
                    parse_binder(pdata, 2);
                    break;

               default:
                    break;
             }
           already_got_size += (size+4);//数据内容加上命令码
          }
          LOGD("====read_buffer process end!====");
       }
    }
    /////////////////////////////////

    return oldIoctl;
}

//实现hook的函数调用
int hook_entry(char * a)
{
    LOGD("Start hooking");
    hook_ioctl();
    return 0;
}

int hook_ioctl()
{
    old_ioctl = (typeof(old_ioctl)) ioctl;
    LOGD("Orig ioctl = %p--------------------------\n", old_ioctl);

    return hook_func_comm("/system/lib/libbinder.so", (void *)ioctl, (void *)new_ioctl);
}

void* get_module_base_address2(pid_t pid, const char* module_name)
{
    FILE *fp;
    long addr = 0;
    char *pch;
    char filename[32];
    char line[1024];

    if (pid < 0)
    {
        /* self process */
        snprintf(filename, sizeof(filename), "/proc/self/maps");
    }
    else
    {
        snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
    }

    fp = fopen(filename, "r");

    if (fp != NULL)
    {
        while (fgets(line, sizeof(line), fp))
        {
            if (strstr(line, module_name))
            {
                pch = strtok(line, "-");
                addr = strtoul(pch, NULL, 16);

                if (addr == 0x8000)
                    addr = 0;

                break;
            }
        }

        fclose(fp);
    }

    return (void *) addr;
}

//传入参数：目标函数(被HOOK的函数)的so路径名，原来ioctl的地址，我们的函数地址
int hook_func_comm(const char *szDestLib, void *oldFunc, void *newFunc){

    void * base_addr = get_module_base_address2(getpid(), szDestLib);  //获取libbinder.so的基地址   参数（进程识别码，libbinder.so路径名）

    int fd;
    fd = open(szDestLib, O_RDONLY);
    if (-1 == fd) {
        LOGD("error\n");
        return -1;
    }

    Elf32_Ehdr ehdr;                                      //ELF header 结构
    read(fd, &ehdr, sizeof(Elf32_Ehdr));

    unsigned long shdr_addr = ehdr.e_shoff;               //Section header table段表 偏移
    int shnum = ehdr.e_shnum;                             //Section header table中有多少个条目
    int shent_size = ehdr.e_shentsize;                    //节头表 每一条目的大小
    unsigned long stridx = ehdr.e_shstrndx;               //包含每一个节名称的字符串表是第几个节（从零开始计数），可以看作是段表数组结构的整体索引

    /* find the section header table */
    Elf32_Shdr shdr;                                      //section header结构体，即段表的每一个条目就是一个Elf32_Shdr结构体
    lseek(fd, shdr_addr + stridx * shent_size, SEEK_SET); //操作ELF文件操作指针  从文件开头把文件指针定位到断表中的字符串表行处
    read(fd, &shdr, shent_size);                          //读取section header table 中的字符串表数据（字符串表实质是段表的一行记录）

    char * string_table = (char *) malloc(shdr.sh_size);
    lseek(fd, shdr.sh_offset, SEEK_SET);                  //so的指针回归到段表开始处
    read(fd, string_table, shdr.sh_size);                 //开辟空间保存读取的字符串名节
    lseek(fd, shdr_addr, SEEK_SET);                       //重置指针指向section header table

    int i;
    uint32_t out_addr = 0;
    uint32_t out_size = 0;
    uint32_t got_item = 0;
    int32_t got_found = 0;

    for (i = 0; i < shnum; i++) {                         //遍历section header table 。对于程序连接器而言，使用sh,对于程序加载器而言使用ph。
        read(fd, &shdr, shent_size);                      //读取一行段表结构，解析该段表信息
        if (shdr.sh_type == SHT_PROGBITS) {               //如果该段和程序相关
            int name_idx = shdr.sh_name;                  //并获取该段名称
            if (strcmp(&(string_table[name_idx]), ".got.plt") == 0
                    || strcmp(&(string_table[name_idx]), ".got") == 0) {
                out_addr = (uint32_t) base_addr + shdr.sh_addr;   //.got表的实际物理加载地址 = libbinder.so的基地址 + offset
                out_size = shdr.sh_size;                          //.got表的大小
                LOGD("out_addr = %x, out_size = %x\n", out_addr, out_size);

                for (i = 0; i < out_size; i += 4) {
                    got_item = *(uint32_t *) (out_addr + i);      //四个字节一个函数或者变量的ITEM，名称占4字节，地址占4字节
                    if (got_item == (uint32_t) oldFunc) {         //找到要hook的函数 ioctl()
                        LOGD("Found s in got\n");
                        got_found = 1;

                        uint32_t page_size = getpagesize();
                        uint32_t entry_page_start = (out_addr + i) & (~(page_size - 1));           //修正地址为页边界地址
                        mprotect((uint32_t *) entry_page_start, page_size,PROT_READ | PROT_WRITE); //对该内存区间的文件提权 ioctl()函数的提权
                        *(uint32_t *) (out_addr + i) = (uint32_t) newFunc;                         //函数地址替换
                        break;
                    } else if (got_item == (uint32_t) newFunc) {
                        LOGD("Already hooked\n");
                        break;
                    }

                }
                if (got_found)
                    break;
            }
        }
    }

    if (!got_found){
        LOGD("hooked failed!!!!!!!!!");
    }

//    SAFE_FREE(string_table);
    if (string_table != NULL)
    {
        free(string_table);
        string_table = NULL;
    }
    close(fd);
    return 0;
}

//解析binder_transaction_data的代码：
void parse_binder(struct binder_transaction_data* pdata, int type)
{
  if(type == 1)//BC 系列命令,target.handle指定到需要访问的Binder对象
  {
    LOGD("binder_transaction_data----->reply to binder: %x, transaction code : %d\n", pdata->target.handle, pdata->code);

  }
  else //从Binder驱动里读回返回值时，需要指定返回的一段地址，target.ptr则会指向这个地址空间
  {
    LOGD("binder_transaction_data----->got from binder: %x, transaction code : %d\n", pdata->target.ptr, pdata->code);

  }
  if(pdata->data_size > 8)//通信数据量比较大，包含很多binder对象。
  {
    int n=0;
    LOGD("binder_transaction_data size > 8");
    for (;n<pdata->offsets_size/4; n++)//获取每一个Binder对象
    {
      LOGD("data_size: %d, buffer: %x, offsets: %x, offset_size: %d, offset %d:%x!",
            pdata->data_size, pdata->data.ptr.buffer, pdata->data.ptr.offsets, pdata->offsets_size, n, ((int*)(pdata->data.ptr.offsets))[n]);
      struct flat_binder_object* pbinder = (struct flat_binder_object*)((char*)pdata->data.ptr.buffer + ((int*)(pdata->data.ptr.offsets))[n]);
      LOGD("got %d binder object in %x!", n, pbinder);
      continue;

      unsigned long type = pbinder->type;
      switch (type)
      {
        case BINDER_TYPE_BINDER:
        case BINDER_TYPE_WEAK_BINDER:
          LOGD("flat_binder_object----->binder:%x, cookie:%x", pbinder->binder, pbinder->cookie);
          break;

        case BINDER_TYPE_HANDLE:
        case BINDER_TYPE_WEAK_HANDLE:
          LOGD("flat_binder_object----->handle:%x\n", pbinder->handle);
          break;
        default:
          break;
      }
    }
  }
  else
  {
    LOGD("flat_binder_object----->binder data : %x %x\n", pdata->data.buf[0], pdata->data.buf[4]);
  }
  return;
}
